(function ($) {
  $.fn.tagcloud = function (options) {
    var opts = $.extend({}, $.fn.tagcloud.defaults, options);
    tagWeights = this.map(function () {
      return $(this).attr("rel");
    });
    tagWeights = jQuery.makeArray(tagWeights).sort(compareWeights);
    lowest = tagWeights[0];
    highest = tagWeights.pop();
    range = highest - lowest;
    if (range === 0) {
      range = 1;
    }
    // Sizes
    if (opts.size) {
      fontIncr = (opts.size.end - opts.size.start) / range;
    }
    // Colors
    if (opts.color) {
      colorIncr = colorIncrement(opts.color, range);
    }
    return this.each(function () {
      weighting = $(this).attr("rel") - lowest;
      if (opts.size) {
        $(this).css({
          "font-size": opts.size.start + weighting * fontIncr + opts.size.unit,
        });
      }
      if (opts.color) {
        // change color to background-color
        $(this).css({
          backgroundColor: tagColor(opts.color, colorIncr, weighting),
        });
      }
    });
  };

  $.fn.tagcloud.defaults = {
    size: { start: 14, end: 18, unit: "pt" },
  };

  // Converts hex to an RGB array
  function toRGB(code) {
    if (code.length == 4) {
      code = jQuery
        .map(/\w+/.exec(code), function (el) {
          return el + el;
        })
        .join("");
    }
    hex = /(\w{2})(\w{2})(\w{2})/.exec(code);
    return [parseInt(hex[1], 16), parseInt(hex[2], 16), parseInt(hex[3], 16)];
  }

  // Converts an RGB array to hex
  function toHex(ary) {
    return (
      "#" +
      jQuery
        .map(ary, function (i) {
          hex = i.toString(16);
          hex = hex.length == 1 ? "0" + hex : hex;
          return hex;
        })
        .join("")
    );
  }

  function colorIncrement(color, range) {
    return jQuery.map(toRGB(color.end), function (n, i) {
      return (n - toRGB(color.start)[i]) / range;
    });
  }

  function tagColor(color, increment, weighting) {
    rgb = jQuery.map(toRGB(color.start), function (n, i) {
      ref = Math.round(n + increment[i] * weighting);
      if (ref > 255) {
        ref = 255;
      } else {
        if (ref < 0) {
          ref = 0;
        }
      }
      return ref;
    });
    return toHex(rgb);
  }

  function compareWeights(a, b) {
    return a - b;
  }
})(jQuery);
